package com.own.stu.apm.agent.core.plugin.interceptor.enhence;

import com.own.stu.apm.agent.core.plugin.AbstractClassEnhencePluginDefine;
import com.own.stu.apm.agent.core.plugin.EnhanceContext;
import com.own.stu.apm.agent.core.plugin.interceptor.ConstructorMethodsInterceptorPoint;
import com.own.stu.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptorPoint;
import com.own.stu.apm.agent.core.plugin.interceptor.StaticMethodsInterceptorPoint;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatcher;

import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.not;

/**
 * 所有的插件都必须直接或间接继承此类,此类完成了增强(transform里指定method和intercept):
 * DynamicType.Builder<?> newBuilder = builder.method(xx).intercept(MethodDelegation.to(xx)
 */
public abstract class ClassEnhancePluginDefine extends AbstractClassEnhencePluginDefine {

    @Override
    protected DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription, DynamicType.Builder<?> builder, ClassLoader classLoader, EnhanceContext enhanceContext) {
        ConstructorMethodsInterceptorPoint[] constructorMethodsInterceptorPoint = getConstructorMethodsInterceptorPoint();
        boolean hasConstructorPoint = true;
        if (constructorMethodsInterceptorPoint == null || constructorMethodsInterceptorPoint.length == 0) {
            hasConstructorPoint = false;
        }

        InstanceMethodsInterceptorPoint[] instanceMethodsInterceptorPoint = getInstanceMethodsInterceptorPoint();
        boolean hasInstancePoint = true;
        if (instanceMethodsInterceptorPoint == null || instanceMethodsInterceptorPoint.length == 0) {
            hasConstructorPoint = false;
        }
        if (!hasConstructorPoint && !hasInstancePoint) {
            return builder;
        }

        /*
        为字节码新增属性,对于同一个typeDescription只需要执行一次
         */
        // typeDescription 不是EnhancedInstance的实现类为真
        if (!typeDescription.isAssignableTo(EnhenceInstanceField.class)) {
            if (!enhanceContext.isObjectExtended()) {
                //为字节码增强属性
                builder = builder.defineField(CONTEXT_ATTR_NAME, Object.class, Opcodes.ACC_PRIVATE | Opcodes.ACC_VOLATILE)
                        .implement(EnhenceInstanceField.class)
                        .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
                enhanceContext.objectExtendedCompleted();
            }
        }

        // 增强构造方法
        if(hasConstructorPoint) {
            for (ConstructorMethodsInterceptorPoint methodsInterceptorPoint : constructorMethodsInterceptorPoint) {
                String methodInterceptor = methodsInterceptorPoint.getConstructorInterceptor();
                if (methodInterceptor == null || "".equals(methodInterceptor)) {
                    throw new RuntimeException("hence class(constructor): " + typeDescription.getTypeName() + " not find interceptor");
                }
                ElementMatcher<MethodDescription> constructorMatcher = methodsInterceptorPoint.getConstructorMatcher();
                builder = builder
                        .constructor(constructorMatcher)
                        .intercept(
                                SuperMethodCall.INSTANCE.andThen(
                                        MethodDelegation.withDefaultConfiguration().to(new ConstructIntercept(methodInterceptor, classLoader))));
            }
        }

        // 增强实例方法
        if(hasInstancePoint) {
            for (InstanceMethodsInterceptorPoint methodsInterceptorPoint : instanceMethodsInterceptorPoint) {

                String methodInterceptor = methodsInterceptorPoint.getMethodInterceptor();
                if (methodInterceptor == null || "".equals(methodInterceptor)) {
                    throw new RuntimeException("hence class(instanceMethod): " + typeDescription.getTypeName() + " not find interceptor");
                }
                ElementMatcher<MethodDescription> staticMethodMatcher = methodsInterceptorPoint.getMethodMatcher();
                builder = builder
                        .method(
                                not(isStatic()).and(staticMethodMatcher)
                        ).intercept(MethodDelegation.withDefaultConfiguration().to(new InstanceMethodIntercept(methodInterceptor, classLoader)));
            }
        }

        return builder;
    }

    @Override
    protected DynamicType.Builder<?> enhanceClass(TypeDescription typeDescription, DynamicType.Builder<?> builder, ClassLoader classLoader) {
        StaticMethodsInterceptorPoint[] staticMethodsInterceptorPoint = getStaticMethodsInterceptorPoint();
        if (staticMethodsInterceptorPoint == null || staticMethodsInterceptorPoint.length == 0) {
            return builder;
        }

        for (StaticMethodsInterceptorPoint methodsInterceptorPoint : staticMethodsInterceptorPoint) {
            String methodInterceptor = methodsInterceptorPoint.getStaticMethodInterceptor();
            if (methodInterceptor == null || "".equals(methodInterceptor)) {
                throw new RuntimeException("hence class(staticMethod): " + typeDescription.getTypeName() + " not find interceptor");
            }
            ElementMatcher<MethodDescription> staticMethodMatcher = methodsInterceptorPoint.getStaticMethodMatcher();
            builder = builder
                    .method(isStatic()
                            .and(staticMethodMatcher)
                    ).intercept(MethodDelegation.withDefaultConfiguration().to(new StaticMethodIntercept(methodInterceptor, classLoader)));
        }

        return builder;
    }
}
